// 类型收窄

// 1. typeof 类型保护（type guards）
function padLeft(padding: number | string, input: string) {
  if (typeof padding === "number") {
    // 在 TS 中，此处 padding 被推导为 number 类型！
    return new Array(padding + 1).join(" ") + input;
  }
  return padding + input;
}

console.log(padLeft(3, "989"));
console.log(padLeft("99,", "900"));

// 2. 真值收窄
function multiplyAll(
  values: number[] | undefined,
  factor: number
): number[] | undefined {
  if (!values) {
    return values;
    // (parameter) values: undefined
  } else {
    return values.map((x) => x * factor);
    // (parameter) values: number[]
  }
}

// 3. 等值收窄
function example(x: string | number, y: string | boolean) {
  if (x === y) {
    x.toUpperCase();
    y.toLowerCase();
  } else {
    console.log(x);
    console.log(y);
  }
}

// 4. in 操作符收窄
type Fish = { swim: () => void };
type Bird = { fly: () => void };

function move(animal: Fish | Bird) {
  if ("swim" in animal) {
    return animal.swim();
    // (parameter) animal: Fish
  }
  return animal.fly();
  // (parameter) animal: Bird
}

// 5. instanceof 操作符收窄
function logValue(x: Date | string) {
  if (x instanceof Date) {
    console.log(x.toUTCString());
  } else {
    console.log(x.toUpperCase());
  }
}

// 6. 赋值收窄
let x: string | number | boolean;
x = "hello";
console.log(x.toUpperCase());

// 7. 类型谓词 is：通过类型谓词，可以将类型收窄到更具体的类型
function isString(val: any): val is string {
  // TS 会自动推导出 val 的类型为 string
  return typeof val === "string";
}

// 8. 可辨别联合（Discriminated unions）

interface Circle {
  kind: "circle";
  radius: number;
}

interface Square {
  kind: "square";
  sideLength: number;
}

type Shape = Circle | Square;

// 在这个例子中，kind 就是这个公共的属性（作为 Shape 的可辨别(discriminant) 属性 ）
function getArea(shape: Shape) {
  if (shape.kind === "circle") {
    return Math.PI * shape.radius ** 2;
  } else {
    return shape.sideLength ** 2;
  }
}

// 9. nerver 类型, 表示永远不会出现的值
function getAreaWithNever(shape: Shape) {
  switch (shape.kind) {
    case "circle":
      return Math.PI * shape.radius ** 2;
    case "square":
      return shape.sideLength ** 2;

    // 只有当Shape的所有类型都被处理完毕
    // default分支中的shape的类型才会是never
    // 当我们给 Shape 类型添加一个新成员，却没有做对应处理的时候，就会导致一个 TypeScript 错误
    default:
      const _exhaustiveCheck: never = shape;
      return _exhaustiveCheck;
  }
}

// 10. satisfies 操作符
type RGB = [red: number, green: number, blue: number];
type Colors = "red" | "green" | "blue";

const palette = {
  red: [255, 0, 0],
  green: "#00ff00",
  blue: [0, 0, 255],
} satisfies Record<Colors, string | RGB>;

palette.red.reduce; // 有了 satisfies 操作符，ts 可以推导出 red 的类型为 RGB
palette.green;
